// Manager Agent
//
// This function represents the logic and activities of the Manager in the movie theater simulation.
// This function will be forked onto a thread during initialization.
//

#include "ManagerAgent.h"
#include "globalsExtern.cc"
#include "utility.h"

void ManagerAgentExecute(int arg) {
	// Manager Agent local data goes here
	

	// Manager Agent running code should go here.
	DEBUG('M', "%s: Thread now running.\n", currentThread->getName());
	
	
	
	managerLock->Acquire();
	managerCondition->Wait(managerLock);
	// Loop Invariant - Some clerk is updated our whiteboard and signaled us. We must evaluate the state and make a decision about how to improve it.
	while (true) {
		// 	if (all theater states are stoppped) {
		DEBUG('M', "%s: Awake, going to make a decision.\n", currentThread->getName());
		
		if (!managerWhiteboard->isMovieRunning()) {
			int takerDoneCount = 0;
			for (int i = 0; i < managerWhiteboard->getNumTakerClerks(); i++) {
				if (managerWhiteboard->getTakerClerkTheaterStatus(i) == managerWhiteboard->TAKER_CLERK_STOPPED) {
					takerDoneCount++;
				}
			}
			if (takerDoneCount == managerWhiteboard->getNumTakerClerks()) {
				do {
					DEBUG('M', "%s: Spinning in waiting loop.\n", currentThread->getName());
					// Wait a short period for the lines to settle
					managerLock->Release();
					for (int i = 0; i < 80; i++) {
						currentThread->Yield();
					}
					managerLock->Acquire();
					
					takerDoneCount = 0;
					for (int i = 0; i < managerWhiteboard->getNumTakerClerks(); i++) {
						if (managerWhiteboard->getTakerClerkTheaterStatus(i) == managerWhiteboard->TAKER_CLERK_STOPPED) {
							takerDoneCount++;
						}
					}
					// if the clerks are no longer ready we must wait again.
				} while(takerDoneCount != managerWhiteboard->getNumTakerClerks());
				// signal to start the movie
				DEBUG('R', "%s is telling the MovieTechnician to start the movie.\n", currentThread->getName());
				movieTechLock->Acquire();
				// update movie running flag
				managerWhiteboard->setMovieRunning(true);
				movieTechCondition->Signal(movieTechLock);
				movieTechLock->Release();
				// clear theater status messages
				for (int i = 0; i < managerWhiteboard->getNumTakerClerks(); i++) {
					managerWhiteboard->setTakerClerkTheaterStatus(i, managerWhiteboard->TAKER_CLERK_TAKING_TICKETS);
				}
			}
		}
		
		// 	if (any ticketClerks have full lines) {
		for(int i = 0; i < managerWhiteboard->getNumTicketClerks(); i++) {
			DEBUG('M', "%s: TicketClerk line status[%d] = %d.\n", currentThread->getName(), i, managerWhiteboard->getTicketClerkLineStatus(i));
			if (managerWhiteboard->getTicketClerkLineStatus(i) == managerWhiteboard->CLERK_LINE_FULL) {
				// if (there are any ticketClerks on break)
				for (int j = 0; j < managerWhiteboard->getNumTicketClerks(); j++) {
					if (managerWhiteboard->getTicketClerkStatus(j) == managerWhiteboard->CLERK_BREAK) {
						DEBUG('M', "%s: Decided take a Ticket Clerk off break.\n", currentThread->getName());
						ticketLineSelectionLock->Acquire();
						// take one off break
						// open their line
						ticketLineSelectionWhiteboard->setLineOpen(j, true);
						// change their status to nominal
						managerWhiteboard->setTicketClerkStatus(j, managerWhiteboard->CLERK_NOMINAL);
						// set numReshuffle for that line to the sum of each line queue
						// Line reshuffleing is currently not working. Delaying as a low priority task.
						/*int totalCustomers = 0;
						for (int k = 0; k < ticketLineSelectionWhiteboard->getNumLines(); k++) {
							totalCustomers += ticketLineSelectionWhiteboard->getLineLength(k);
							// broadcast to all customers in that line to reshuffle
							ticketLineSelectionCondition[k]->Broadcast(ticketLineSelectionLock);
							// reset the line lengths on the line selection whiteboard
							//ticketLineSelectionWhiteboard->setLineLength(k, 1); // magic number, it is because the current customer will decude the number before the next customers get there
							// reset all line status to prevent bringing more than 1 clerk off break
							managerWhiteboard->setTicketClerkLineStatus(k, managerWhiteboard->CLERK_LINE_NORM);
						}
						ticketLineSelectionWhiteboard->setNumReshuffle(totalCustomers);*/
						managerWhiteboard->setTicketClerkLineStatus(i, managerWhiteboard->CLERK_LINE_NORM);
						ticketLineSelectionLock->Release();
						
						DEBUG('M', "%s: Ticket lines are long, Took a Clerk off break.\n", currentThread->getName());
						break;
					}
				}
				// reached a decision, going back to sleep
				managerWhiteboard->setTicketClerkLineStatus(i, managerWhiteboard->CLERK_LINE_NORM);
				DEBUG('M', "%s: Ticket lines are long, but no other clerks to bring off break.\n", currentThread->getName());
				break;
			}
		}
		
		
		for (int i = 0; i < managerWhiteboard->getNumTicketClerks(); i++) {
			//	else if (any ticketClerks have short lines && another ticket clerk is working) {
			if (managerWhiteboard->getTicketClerkLineStatus(i) == managerWhiteboard->CLERK_LINE_SHORT) {
				DEBUG('M', "%s: Noticed a TicketClerk with a short line.\n", currentThread->getName());
				for (int j = 0; j < managerWhiteboard->getNumTicketClerks(); j++) {
					if (i != j && managerWhiteboard->getTicketClerkStatus(j) != managerWhiteboard->CLERK_BREAK) {
						DEBUG('M', "%s: Decided to put a TicketClerk on break.\n", currentThread->getName());
						// 20% chance
						if (rand() % 1000 + 1 <= 200) {
							ticketLineSelectionLock->Acquire();
							// close their line
							ticketLineSelectionWhiteboard->setLineOpen(i, false);
							managerWhiteboard->setTicketClerkLineStatus(i, managerWhiteboard->CLERK_LINE_NORM);
							// change their status to break
							managerWhiteboard->setTicketClerkStatus(i, managerWhiteboard->CLERK_BREAK);
							ticketLineSelectionLock->Release();
						}
						break;
					}
				}
			}
		}
		
		/*processClerkStatus(ticketClerkStatus, ManagerWhiteboard->getNumTicketClerks(), ticketLineSelectionWhiteboard, ticketLineSelectionLock, ticketLineSelectionCondition);
		processClerkStatus(foodClerkStatus, ManagerWhiteboard->getNumFoodClerks(), foodLineSelectionWhiteboard, foodLineSelectionLock, foodLineSelectionCondition);
		processClerkStatus(takerClerkStatus, ManagerWhiteboard->getNumTakerClerks(), takerLineSelectionWhiteboard, takerLineSelectionLock, takerLineSelectionCondition);*/
		
		// reached a decision, going back to sleep
		DEBUG('M', "%s: Reached a decision, going back to sleep.\n", currentThread->getName());
		
		totalsLock->Acquire();
		DEBUG('R',"Total money made by office = $%d.\n", totalsWhiteboard->getTotalRevenue());
		for (int i=0; i< managerWhiteboard->getNumTicketClerks(); i++){
			DEBUG('R',"Manager collected $%d from TicketClerk %d.\n", totalsWhiteboard->getTicketClerkRevenue(i),i);
		}
		for (int i=0; i< managerWhiteboard->getNumFoodClerks(); i++){
			DEBUG('R',"Manager collected $%d from FoodClerk %d.\n", totalsWhiteboard->getFoodClerkRevenue(i),i);
		}
		totalsLock->Release();
		
		managerCondition->Wait(managerLock);
	}
}

/*void processClerkStatus(int[] status, int numClerks, LineSelectionWhiteboard whiteboard, Lock* lineLock, Condition[] lineConditions) {
	// 	if (any ticketClerks have full lines) {
	for(int i = 0; i < numClerks; i++) {
		if (status[i] == ManagerWhiteboard.CLERK_LINE_FULL) {
			// if (there are any ticketClerks on break)
			for (int j = 0; j < numClerks; j++) {
				if (status[j] == ManagerWhiteboard.CLERK_BREAK) {
						lineLock->Acquire();
						// take one off break
						// open their line
						whiteboard->setLineOpen(j, true);
						// change their status to nominal
						status[j] = ManagerWhiteboard.CLERK_NOMINAL
						// set numReshuffle for that line to the sum of each line queue
						int totalCustomers = 0;
						for (int k = 0; k < whiteboard->getNumLines(); k++) {
							totalCustomers += whiteboard->getLineLength(k);
							// broadcast to all customers in that line to reshuffle
							lineConditions[k]->Broadcast(lineLock);
						}
						whiteboard->setNumReshuffle(totalCustomers);
				}
			}
			return;
		}
	}
	
	
	for (int i = 0; i < numClerks; i++) {
		//	else if (any ticketClerks have short lines && another ticket clerk is working) {
		if (status[i] == ManagerWhiteboard.CLERK_LINE_SHORT) {
			for (int j = 0; j < numClerks; j++) {
				if (i != j && status[j] != ManagerStatus.CLERK_BREAK) {
					// 20% chance
					if (rand() <= 0.2) {
						lineLock->Acquire();
						// close their line
						whiteboard->setLineOpen(i, false);
						// change their status to break
						status[i] = ManagerWhiteboard.CLERK_BREAK;
						lineLock->Release();
					}
					break
				}
			}
		}
	}
}*/
